home *** CD-ROM | disk | FTP | other *** search
- /* File MSNTND.C
- * Telnet driver
- *
- * Copyright (C) 1985, 1993, Trustees of Columbia University in the
- * City of New York. Permission is granted to any individual or institution
- * to use this software as long as it is not sold for profit. This copyright
- * notice must be retained. This software may not be included in commercial
- * products without written permission of Columbia University.
- *
- * Written for MS-DOS Kermit by Joe R. Doupnik,
- * Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet,
- * and by Frank da Cruz, Columbia Univ., fdc@watsun.cc.columbia.edu.
- * With earlier contributions by Erick Engelke of the University of
- * Waterloo, Waterloo, Ontario, Canada.
- *
- * Last edit
- * 16 June 1993 v3.13
- */
- #include "msntcp.h"
- #include "msnlib.h"
-
- #define MAXSESSIONS 6
- #define MSGBUFLEN 512
-
- /* TCP/IP Telnet negotiation support code */
- #define IAC 255
- #define DONT 254
- #define DO 253
- #define WONT 252
- #define WILL 251
- #define SB 250
- #define BREAK 243
- #define SE 240
-
- #define TELOPT_ECHO 1
- #define TELOPT_SGA 3
- #define TELOPT_STATUS 5
- #define TELOPT_TTYPE 24
- #define TELOPT_NAWS 31
- #define NTELOPTS 24
-
- #define BAPICON 0xa0 /* 3Com BAPI, connect to port */
- #define BAPIDISC 0xa1 /* 3Com BAPI, disconnect */
- #define BAPIWRITE 0xa4 /* 3Com BAPI, write block */
- #define BAPIREAD 0xa5 /* 3Com BAPI, read block */
- #define BAPIBRK 0xa6 /* 3Com BAPI, send short break */
- #define BAPISTAT 0xa7 /* 3Com BAPI, read status (# chars avail) */
- #define BAPIHERE 0xaf /* 3Com BAPI, presence check */
- #define BAPIEECM 0xb0 /* 3Com BAPI, enable/disable ECM char */
- #define BAPIECM 0xb1 /* 3Com BAPI, trap Enter Command Mode char */
- #define BAPIPING 0xb2 /* Send Ping, Kermit extension of BAPI */
- #define BAPISTAT_SUC 0 /* function successful */
- #define BAPISTAT_NCW 1 /* no character written */
- #define BAPISTAT_NCR 2 /* no character read */
- #define BAPISTAT_NSS 3 /* no such session */
- #define BAPISTAT_NOS 7 /* session aborted */
- #define BAPISTAT_NSF 9 /* no such function */
-
-
- #define TSBUFSIZ 41
- static byte sb[TSBUFSIZ]; /* Buffer for subnegotiations */
-
- static byte *termtype; /* Telnet, used in negotiations */
- static int sgaflg = 0; /* Telnet SGA flag */
- static int dosga = 0; /* Telnet 1 if I sent DO SGA from tn_ini() */
- static int wttflg = 0; /* Telnet Will Termtype flag */
- static int wnawsflg = 0; /* Telnet Will NAWS option flag */
- static ttinccnt = 0; /* Telnet count of chars in read socket */
- static int in_IAC = 0; /* non-zero when doing Telnet Options*/
- int echo = 1; /* Telnet echo, default on, but hate it */
-
- struct {
- word ident;
- char *name;
- } termarray[]= /* terminal type names, from mssdef.h */
- {
- {0,"UNKNOWN"}, {1,"H-19"}, {2,"VT52"}, {4,"VT100"},
- {8,"VT102"}, {0x10,"VT220"}, {0x20,"VT320"}, {0x40,"TEK4014"},
- {0x80,"VIP7809"}, {0x100, "PT200"}, {0x200, "D463"},
- {0x400, "D470"}, {0xff,"UNKNOWN"}
- };
-
- extern byte FAR * bapiadr; /* Far address of local Telnet client's buf */
- extern int bapireq, bapiret; /* count of chars in/out */
- extern longword my_ip_addr, sin_mask; /* binary of our IP, netmask */
- extern longword ipbcast; /* binary IP of broadcast */
- extern byte * def_domain; /* default domain string */
- extern byte * hostname; /* our name from BootP server */
- static longword host; /* binary IP of host */
- int doslevel = 1; /* operating at DOS level if != 0 */
- word sock_delay = 30;
- static tcp_Socket *s; /* ptr to active socket */
- int msgcnt; /* count of chars in message buffer */
- byte msgbuf[MSGBUFLEN+1]; /* message buffer for client */
-
- extern int hookvect(void);
- extern int unhookvect(void);
- extern int kecho(int);
- extern void readback(void);
- int session_close(int);
- int session_rotate(int);
- int tn_ini(void);
- int ttinc(void);
- int tn_doop(word);
- int send_iac(byte, int);
- int tn_sttyp(void);
- int tn_snaws(void);
- int subnegotiate(void);
- void optdebug(byte, int);
-
- extern byte kmyip[]; /* our IP number */
- extern byte knetmask[]; /* our netmask */
- extern byte kdomain[]; /* our domain */
- extern byte kgateway[]; /* our gateway */
- extern byte kns1[]; /* our nameserver #1 */
- extern byte kns2[]; /* our nameserver #2 */
- extern byte kbcast[]; /* broadcast address pattern */
- extern byte khost[]; /* remote host name/IP # */
- extern word kport; /* remote host TCP port */
- extern word kserver; /* if Kermit is in server mode */
- extern byte ktttype[]; /* user term type override string */
- extern word kterm; /* terminal type index, from mssdef.h*/
- extern byte kterm_lines; /* terminal screen height */
- extern byte kterm_cols; /* terminal screen width */
- extern byte kbtpserver[]; /* IP of Bootp host answering query */
- extern byte kdebug; /* non-zero if debug mode is active */
-
- static struct sessioninfo
- {
- tcp_Socket socketdata; /* current socket storage area */
- int sessionid; /* identifying session */
- int tn_inited; /* Telnet Options initied */
- int echo; /* local echo state (0=remote) */
- } session[MAXSESSIONS];
-
- static unsigned num_sessions = 0; /* qty of active sessions */
- static int active = -1; /* ident of active session */
-
- /* this runs at Kermit task level */
- int
- main(void) /* start TCP from Kermit */
- {
- int status = 0;
- register byte *p;
- register int i;
-
- doslevel = 1; /* say at DOS level here */
- if (num_sessions == 0) /* then initialize the system */
- {
- my_ip_addr = 0L;
- def_domain = kdomain; /* define our domain */
- host = 0L; /* BELONGS IN SESSION, but is global */
- for (i = 0; i < MAXSESSIONS; i++) /* init sessioninfo array */
- session[i].sessionid = -1;
-
- if (hookvect() == 0)
- {
- outs("\r\n Hooking vectors failed");
- goto anyerr;
- }
-
- s = NULL; /* no TCP socket yet */
- if (tcp_init() == 0) /* init TCP code */
- {
- outs("\r\n Unable to initialize TCP/IP system, quitting");
- goto anyerr;
- }
-
- /* set Ethernet broadcast to all 1's or all 0's */
- ipbcast = resolve(kbcast); /* IP broadcast address */
- bootphost = ipbcast; /* set Bootp to this IP too */
-
- if ((p = strchr(kmyip, ' ')) != NULL) /* have a space */
- *p = '\0'; /* terminate on the space */
-
- if (stricmp(kmyip, "bootp") == 0)
- {
- if (dobootp() != 0) /* use BOOTP for Internet address */
- {
- outs("\r\n BOOTP query failed. Quitting.");
- goto anyerr;
- }
- ntoa(kmyip, my_ip_addr);
- if (sin_mask != 0L)
- ntoa(knetmask, sin_mask);
- if (arp_rpt_gateway(0) != 0L)
- ntoa(kgateway, arp_rpt_gateway(0));
- if (last_nameserver > 0)
- ntoa(kns1, def_nameservers[0]);
- if (last_nameserver > 1)
- ntoa(kns2, def_nameservers[1]);
- if (kdomain[0] == '\0' && hostname[0] != '\0')
- /* construct domain */
- if (p = strchr(hostname, '.')) /* find dot */
- strcpy(kdomain, &p[1]);
- readback(); /* readback these to Kermit main */
- }
- else
- {
- if (stricmp(kmyip, "rarp") == 0)
- {
- my_ip_addr = 0L; /* clear our IP for RARP */
- if (pkt_rarp_init() == 0)
- goto anyerr; /* no RARP handle */
-
- if (do_rarp() == 0) /* use RARP */
- {
- outs("\r\n RARP query failed.");
- goto anyerr;
- }
- ntoa(kmyip, my_ip_addr);
- readback();
- }
- else /* convert to 32 bit binary */
- my_ip_addr = resolve(kmyip);
- }
-
- if (my_ip_addr == 0L)
- { /* something drastically wrong */
- outs("\r\n Cannot understand my IP address, terminating");
- goto anyerr;
- }
-
- if ((sin_mask = resolve(knetmask)) == 0L)
- { /* something drastically wrong */
- outs("\r\n Bad network submask, terminating");
- goto anyerr;
- }
-
- if (stricmp(kns1, "unknown"))
- add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
- resolve(kns1));
- if (stricmp(kns2, "unknown"))
- add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
- resolve(kns2));
-
- if (stricmp(kgateway, "unknown"))
- arp_add_gateway(kgateway, 0L);
-
- } /* end of initial system setup */
-
- /* starting a new session */
-
- if (num_sessions >= MAXSESSIONS)
- {
- outs("\nAll sessions are in use. Sorry\n");
- return (-1); /* say can't do another, fail */
- }
-
- status = -1;
- for (i = 0; i < MAXSESSIONS; i++) /* find free ident */
- if (session[i].sessionid < 0)
- {
- s = &session[i].socketdata; /* active socket */
- session[i].sessionid = i; /* ident */
- status = i;
- active = i; /* identify active session */
- session[i].echo = echo; /* Telnet echo from mainpgm */
- session[i].tn_inited = 0; /* do Telnet Options init */
- num_sessions++; /* say have another active session */
- break;
- }
-
- if (status == -1)
- goto sock_err; /* report bad news and exit */
- status = 0;
-
- if (ktttype[0] != '\0') /* if user override given */
- termtype = ktttype;
- else /* get termtype from real use */
- {
- for (i = 0; termarray[i].ident != 0xff; i++)
- if (termarray[i].ident == kterm) /* match */
- break;
- termtype = termarray[i].name;
- }
-
- /* kserver is set by main body SERVER command. If khost[0] is '*'
- then also behave as a server/listener for any main body mode. */
-
- if ((khost[0] == '*') || (kserver != 0))
- {
- host = 0L; /* force no host IP at this stage */
- tcp_listen(s, kport, host, 0, 0); /* post a listen */
- }
- else /* normal client mode */
- {
- outs("\r\n Resolving address of host ");
- outs(khost); outs(" ...");
- if ((host = resolve(khost)) == 0)
- {
- outs( "\r\n Cannot resolve address of host ");
- outs(khost);
- goto anyerr;
- }
- outs("\r\n"); /* get clean screen line */
- }
-
- if (host == my_ip_addr)
- {
- outs("\r\n Cannot talk to myself, sorry.");
- goto anyerr;
- }
-
- if ((khost[0] == '*') || (kserver != 0)) /* if server */
- {
- doslevel = 0; /* end of DOS level i/o */
- if (kserver == 0)
- outs("\r\n Operating as a Telnet server. Waiting...");
- }
- else /* we are a client */
- {
- if (tcp_open(s, 0, host, kport) == 0)
- {
- outs("\r\n Unable to contact the host.");
- outs("\r\n The host may be down or");
- outs(" a gateway may be needed.");
- goto anyerr;
- }
- sock_wait_established(s, sock_delay, NULL, &status);
- }
-
- if (sock_mode(s, TCP_MODE_NAGLE) == 0)
- {
- outs("\r\n Unable to set socket mode");
- goto anyerr;
- }
- doslevel = 0; /* end of DOS level i/o */
- return (active); /* return handle of this session */
-
-
- sock_err:
- switch (status)
- {
- case 1 : outs("\r\n Session is closed");
- break;
- case -1:/* outs("\r\n Cannot start a connection");*/
- break;
- }
-
- anyerr: session_close(active);
- if (num_sessions == 0)
- {
- tcp_shutdown();
- eth_release(); /* do absolutely */
- unhookvect();
- doslevel = 1; /* at DOS level i/o */
- }
- return (-1); /* say have stopped */
- }
-
- /* this runs at Kermit task level */
- int
- exit(int value) /* stop TCP from Kermit */
- {
- int i;
-
- doslevel = 1; /* tell i/o routines this is DOS */
- for (i = 0; i < MAXSESSIONS; i++)
- session_close(i); /* close session, free buffers */
- num_sessions = 0;
- tcp_shutdown(); /* force the issue if necessary */
- eth_release(); /* do absolutely */
- unhookvect();
- return (-1);
- }
-
- /* return the int of the next available session after session "ident", or
- -1 if none. Do not actually change sessions.
- */
- int
- session_rotate(int ident)
- {
- register int i, j;
- for (i = 1; i <= MAXSESSIONS; i++)
- {
- j = (i + ident) % MAXSESSIONS; /* modulo maxsessions */
- if (session[j].sessionid != -1)
- return (j);
- }
- return (-1);
- }
-
- /* Change to session ident "ident" and active to "ident", return "active"
- if that session is valid, else return -1.
- */
- int
- session_change(int ident)
- { /* change active session to ident */
-
- if (ident < 0 || ident >= MAXSESSIONS)
- return (-1);
-
- if (session[ident].sessionid < 0)
- return (-1); /* inactive session */
-
- s = &session[ident].socketdata; /* watch mid-stream stuff */
- kecho(echo = session[ident].echo); /* update Kermit main body */
- return (active = ident); /* ident of active session */
- }
-
- /* Close session "ident". Return ident of next available session, if any,
- without actually changing sessions. Returns -1 if no more sessions or
- if ident is out of legal range or if the session is already closed.
- */
- int
- session_close(int ident) /* close a particular, ident, session */
- {
- if (ident >= 0 && ident < MAXSESSIONS &&
- session[ident].sessionid >= 0) /* graceful close */
- {
- sock_close(&session[ident].socketdata);
- /* free receive and send socket data buffers */
- /* this is pushing the tcp timeout a bit much */
- free(session[ident].socketdata.rdata);
- free(session[ident].socketdata.sdata);
- /* clear pointers to same */
- session[ident].socketdata.rdata = NULL;
- session[ident].socketdata.sdata = NULL;
- session[ident].sessionid = -1; /* marked closed */
- session[ident].tn_inited = 0; /* not inited */
- if (num_sessions > 0) num_sessions--;/* qty active sessions */
- }
- /* activate, rtn next available session */
- return (session_change(session_rotate(ident)));
- }
-
- /* This is called by the main body of Kermit to transfer data. It returns
- the transfer status as a BAPI valued int. */
-
- int
- serial_handler(word cmd)
- {
- int i, cmdstatus;
- register int ch;
- byte FAR * bp;
- extern int session_change(int);
-
- if (session[active].tn_inited == 0) /* if not initialized yet */
- if (tn_ini() == -1) /* init Telnet negotiations */
- return (BAPISTAT_NOS); /* fatal error, quit */
-
- tcp_tick(s); /* catch up on packet reading */
-
- cmdstatus = BAPISTAT_SUC; /* success so far */
- switch (cmd) /* cmd is function code */
- {
- case BAPIWRITE: /* write a block, bapireq chars */
- if (khost[0] == '*' && s->state != tcp_StateESTAB)
- {
- bapiret = bapireq; /* discard output and */
- break; /* send nothing until client appears*/
- }
- if (session[active].echo)
- if (sock_flushnext(s) == 0) /* send next now */
- cmdstatus = BAPISTAT_NOS; /* no sess*/
- if (s->state == tcp_StateESTAB)
- bapiret = sock_write(s, bapiadr, bapireq);
- else
- {
- cmdstatus = BAPISTAT_NOS; /* no session */
- bapiret = bapireq; /* discard data */
- break;
- }
-
- /* if terminal serving with no local echoing do echo here */
- if (session[active].echo == 0 &&
- khost[0] == '*' && kserver == 0)
- { /* echo to us */
- i = bapiret > MSGBUFLEN-msgcnt?
- MSGBUFLEN-msgcnt: bapiret;
- bcopyff(bapiadr, msgbuf, i);
- outsn(msgbuf, i);
- }
- break;
-
- case BAPIREAD: /* read block, count of bapireq */
- /* is IAC present? */
- i = fstchr(s->rdata, s->rdatalen, IAC);
- bapiret = 0;
-
- if (i < 0)
- {
- cmdstatus = BAPISTAT_NCR;/* nothing present */
- break;
- }
- if (i > 0 && in_IAC == 0) /* read up to IAC */
- {
- if (i > bapireq) i = bapireq;
- bapiret = sock_fastread(s, bapiadr, i);
-
- /* if terminal serving with local echoing then echo to host */
- if (session[active].echo != 0 &&
- khost[0] == '*' &&
- kserver == 0)
- sock_write(s, bapiadr, bapiret);
- break;
- }
-
- bp = bapiadr; /* use slow reading for Options */
- ttinccnt = 0; /* setup to read from buffer */
- /* state is here, IAC style, vs fast mode */
- in_IAC = 1;
- for (i = 0; i < bapireq; i++)
- {
- if ((ch = ttinc()) == -1)
- break; /* no char */
- ch &= 0xff;
- if ( ch == IAC ) /* Telnet options intro */
- { /* do Options */
- if ((ch = ttinc()) == -1)
- break; /* do more later */
- if ((ch &= 0xff) != IAC)
- { /* IAC IAC means IAC */
- tn_doop(ch); /* negotiate */
- continue;
- }
- }
- in_IAC = 0; /* end of mode */
- *bp++ = (byte) (ch & 0xff);
- bapiret++;
- }
- session[active].echo = echo;
- break;
-
- case BAPIBRK: /* send BREAK */
- sock_putc(s, IAC); /* char to socket buffer */
- sock_putc(s, BREAK);
- if (sock_flush(s) == 0) /* send it now, with push */
- cmdstatus = BAPISTAT_NOS; /* no session */
- break;
-
- case BAPIPING: /* Ping current host */
- do_ping(khost, host);
- break;
-
- case BAPISTAT: /* check read status (chars avail) */
- bapiret = sock_dataready(s); /* # chars available */
- break;
-
- case BAPIDISC: /* close this connection */
- session_close(active);
- /* fall through to do session rotate */
- case BAPIECM:
- i = session_rotate(active); /* get new ident */
- if (i != -1) /* if exists */
- i = session_change(i); /* make active */
- bapiret = 0; /* no chars processed */
- return (i); /* New session or -1, special */
-
- default: cmdstatus = BAPISTAT_NSF;/* unsupported function */
- break;
- }
-
- if ((sock_dataready(s) == 0) && (msgcnt == 0) &&
- (s->sisopen == SOCKET_CLOSED)) /* no data and no session */
- return (BAPISTAT_NOS); /* means exit session */
- else
- return (cmdstatus); /* stuff is yet unread */
- }
-
- /* ttinc - destructively read char from socket buffer, return -1 if fail */
-
- int
- ttinc(void)
- {
- byte ch;
-
- if (ttinccnt <= 0) /* if no known chars */
- {
- tcp_tick(s); /* read another packet */
- ttinccnt = sock_dataready(s); /* count available, if any */
- }
- if (sock_fastread(s, &ch, 1) != 0) /* qty chars returned */
- {
- ttinccnt--; /* one less char in socket */
- return (0xff & ch);
- }
- return (-1);
- }
-
- /* Initialize a telnet connection */
- /* Returns -1 on error, 0 is ok */
-
- int
- tn_ini(void)
- {
- sgaflg = 0; /* SGA flag starts out this way */
- wttflg = 0; /* Did not send WILL TERM TYPE yet. */
- wnawsflg = 0; /* Did not send NAWS info yet */
- dosga = 0; /* Did not send DO SGA yet. */
- in_IAC = 0; /* haven't done IAC reception yet */
- ttinccnt = 0; /* count of chars in read socket */
- session[active].tn_inited = 1; /* say we are doing this proc */
- kecho(echo = 1); /* start with echo to ourselves */
- /* if not server or not Telnet port */
- if (khost[0] == '*' || kserver != 0 || kport != 23)
- return (0); /* don't go first */
- if (send_iac( WILL, TELOPT_TTYPE )) return( -1 );
- if (send_iac( WILL, TELOPT_NAWS)) return(-1);
- if (send_iac( DO, TELOPT_SGA )) return( -1 );
- wttflg = 1; /* Remember I offered TTYPE */
- wnawsflg = 1; /* Remember I offered NAWS */
- dosga = 1; /* Remember I sent DO SGA */
- return(0);
- }
-
- /*
- * send_iac - send interupt character and pertanent stuff
- * - return 0 on success
- */
-
- int
- send_iac(byte cmd, int opt)
- {
- byte io_data[3];
-
- io_data[0] = IAC;
- io_data[1] = cmd;
- io_data[2] = (byte)(opt & 0xff);
- if (sock_fastwrite(s, io_data, 3) == 0)
- return (1); /* failed to write */
- if (kdebug != 0)
- {
- outs("Opt send ");
- optdebug(cmd, opt);
- if (cmd != SB) outs("\r\n");
- }
-
- tcp_tick(s);
- return (0);
- }
-
- /*
- * Process in-band Telnet negotiation characters from the remote host.
- * Call with the telnet IAC character and the current duplex setting
- * (0 = remote echo, 1 = local echo).
- * Returns:
- * -1 on success or char 0x255 (IAC) when IAC is the first char read here.
- */
-
- int
- tn_doop(word ch)
- { /* enter after reading IAC char */
- register int c, x;
-
- if (ch < SB) return(0); /* ch is not in range of Options */
-
- if ((x = ttinc()) == -1) /* read Option character */
- return (-1); /* nothing there */
- x &= 0xff;
- c = ch; /* use register'd character */
-
- if (kdebug != 0)
- {
- outs("Opt recv ");
- optdebug((byte)c, x);
- if (c != SB) outs("\r\n");
- }
-
- switch (x) {
- case TELOPT_ECHO: /* ECHO negotiation */
- if (c == WILL) /* Host says it will echo */
- {
- if (echo != 0) /* reply only if change required */
- {
- send_iac(DO,x); /* Please do */
- kecho(echo = 0); /* echo is from the other side */
- }
- break;
- }
-
- if (c == WONT) /* Host says it won't echo */
- {
- if (echo == 0) /* If we not echoing now */
- {
- send_iac(DONT,x); /* agree to no host echo */
- kecho(echo = 1); /* do local echoing */
- }
- break;
- }
-
- if (c == DO)
- { /* Host wants me to echo to it */
- send_iac(WONT,x); /* I say I won't */
- break;
- }
- break; /* do not respond to DONT */
-
- case TELOPT_SGA: /* Suppress Go-Ahead */
- if (c == WONT) /* Host says it won't sup go-aheads */
- {
- if (sgaflg == 0)
- send_iac(DONT, x); /* acknowledge */
- sgaflg = 1; /* suppress, remember */
- if (echo == 0) /* if we're not echoing, */
- kecho(echo = 1); /* switch to local echo */
- break;
- }
-
- if (c == WILL) /* Host says it will use go aheads */
- {
- if (sgaflg || !dosga) /* ACK only if necessary */
- {
- sgaflg = 0; /* do go-aheads, remember */
- send_iac(DO,x); /* this is a change, so ACK */
- }
- break;
- }
- break; /* no response to other cases */
-
- case TELOPT_TTYPE: /* Terminal Type */
- switch (c) {
- case DO: /* DO terminal type */
- if (wttflg == 0) { /* If I haven't said so before, */
- send_iac(WILL, x); /* say I'll send it if asked */
- wttflg++;
- }
- break;
-
- case SB: /* enter subnegotiations */
- if (wttflg == 0)
- break; /* we have not been introduced yet */
- if (subnegotiate() != 0) /* successful negotiation */
- tn_sttyp(); /* report terminal type */
- break;
-
- default: /* ignore other TTYPE Options */
- break;
- } /* end of inner switch (c) */
- break;
-
- case TELOPT_NAWS: /* terminal width and height */
- switch (c) {
- case DO: /* DO terminal type */
- if (wnawsflg == 0) { /* If I haven't said so before, */
- send_iac(WILL, x); /* say I'll send it if asked */
- wnawsflg++;
- }
- break;
-
- case SB: /* enter subnegotiations */
- if (wnawsflg == 0)
- break; /* we have not been introduced yet */
- if (subnegotiate() != 0) /* successful negotiation */
- tn_snaws(); /* report screen size */
- break;
-
- default: /* ignore other NAWS Options */
- break;
- } /* end of inner switch (c) */
- break;
-
-
- default: /* all other Options: refuse nicely */
- switch(c) {
- case WILL: /* You will? */
- send_iac(DONT,x); /* Please don't */
- break;
- case DO: /* You want me to? */
- send_iac(WONT,x); /* I won't */
- break;
-
- case DONT:
- send_iac(WONT,x); /* I won't */
- break;
-
- case WONT: /* You won't? */
- break; /* Good */
-
- default:
- break; /* unknown character, discard */
- } /* end of default switch (c) */
- break;
- } /* end switch (x) */
- return (-1); /* say done with Telnet Options */
- }
-
- /* Perform Telnet Option subnegotiation. SB byte has been read. Consume
- through IAC SE. Return 1 if successful, else 0.
- */
- int
- subnegotiate(void)
- {
- register word flag, y;
- word n;
-
- n = flag = 0; /* flag for when done reading SB */
- while (n < TSBUFSIZ)
- { /* loop looking for IAC SE */
- if ((y = ttinc()) == -1)
- break; /* nothing there */
- y &= 0xff; /* make sure it's just 8 bits */
- sb[n++] = (byte) y; /* save what we got in buffer */
-
- if (kdebug != 0)
- {
- if (y == SE) outs(" se\r\n");
- else
- if (y != IAC)
- {
- if (n == 1 && y == 1)
- outs(" send");
- else
- {
- outs(" \\x");
- outhex((byte)y);
- }
- }
- }
-
- if (y == IAC) /* If this is an IAC */
- {
- if (flag) /* If previous char was IAC */
- {
- n--; /* it's quoted, keep one IAC */
- flag = 0; /* and turn off the flag. */
- }
- else flag = 1; /* Otherwise set the flag. */
- }
- else if (flag) /* Something else following IAC */
- {
- if (y != SE) /* If not SE, it's a protocol error */
- flag = 0;
- break;
- } /* end of if (y == IAC) */
- } /* end while */
-
- if (flag == 0 || y == -1) /* no option IAC SE */
- return (0); /* flag == 0 is invalid SB */
-
- if ( *sb == 1 ) /* wants us to report option */
- return (1); /* say can do report */
- else
- return (0);
- }
-
- /* Telnet send terminal type */
- /* Returns -1 on error, 0 on success */
-
- int
- tn_sttyp(void)
- { /* Send telnet terminal type. */
- register byte *ttn;
- register int ttnl; /* Name & length of terminal type. */
-
- ttn = termtype; /* we already got this from environment */
- if ((*ttn == 0) || ((ttnl = strlen(ttn)) >= TSBUFSIZ)) {
- ttn = "UNKNOWN";
- ttnl = 7;
- }
-
- ttn = strcpy(&sb[1], ttn); /* Copy to subnegotiation buffer */
- while (*ttn != NULL)
- {
- if (*ttn >= 'a' && *ttn <= 'z') *ttn += (byte)('A' - 'a');
- ttn++;
- }
- *sb = 0; /* 'is'... */
- *ttn++ = IAC;
- *ttn = SE;
-
- send_iac((byte)SB, TELOPT_TTYPE); /* Send: Terminal Type */
- sock_flushnext(s); /* send on next write */
- sock_fastwrite(s, sb, ttnl + 3);
- if (kdebug != 0)
- {
- int i;
- outs(" "); for (i = 0; i < ttnl; i++) outch(sb[i+1]);
- outs(" se\r\n");
- }
- return (0);
- }
-
- /* Send terminal width and height (characters). RFC 1073 */
- int
- tn_snaws(void)
- {
- char sbuf[6] = {0,0, 0,0, (char)IAC, (char)SE};
-
- sbuf[1] = kterm_cols;
- sbuf[3] = kterm_lines;
- send_iac((byte)SB, TELOPT_NAWS); /* Send: Terminal size */
- sock_flushnext(s); /* send on next write */
- sock_fastwrite(s, sbuf, 6);
- if (kdebug != 0)
- {
- outs(" \\x");
- outhex(sbuf[1]);
- outs(" \\x");
- outhex(sbuf[3]);
- outs(" se\r\n");
- }
- return (0);
- }
-
- /* assist displaying of Telnet Options negotiation material */
- void
- optdebug(byte cmd, int option)
- {
- switch (cmd)
- {
- case WILL: outs("will ");
- break;
- case WONT: outs("wont ");
- break;
- case DO: outs("do ");
- break;
- case DONT: outs("dont ");
- break;
- case SB: outs("sb ");
- break;
- default: outs("\\x "); outhex(cmd);
- break;
- } /* end of switch c */
- switch (option)
- {
- case TELOPT_ECHO: outs("echo");
- break;
- case TELOPT_SGA: outs("sga");
- break;
- case TELOPT_TTYPE: outs("ttype");
- break;
- case TELOPT_NAWS: outs("naws");
- break;
- default: outs("\\x"); outhex((byte)option);
- break;
- }
- }
-
-
- /* Compose a nice greeting message for incoming Telnet connections. Called
- by tcp_handler() in the tcp_StateLISTEN section. It also notifies the
- local terminal emulator of the client's presence and address. */
-
- void
- server_hello(tcp_Socket *s)
- {
- char hellomsg[MSGBUFLEN]; /* work buffer, keep short */
- register int len;
-
- strcpy(hellomsg,
- "\r\n Welcome to the MS-DOS Kermit Telnet server at [");
- ntoa(&hellomsg[strlen(hellomsg)], my_ip_addr); /* our IP */
- strcat(hellomsg, "].\r\n"); /* as [dotted decimal] */
- if (kserver != 0) /* if file serving */
- {
- strcat(hellomsg," Escape back to your Kermit prompt and");
- strcat(hellomsg," issue Kermit file server commands.\r\n\n");
- }
- else /* if terminal emulating */
- {
- strcat(hellomsg,
- " You are talking to the terminal emulator,\r\n");
- strcat(hellomsg, " adjust local echoing accordingly.\r\n");
- }
-
- /* stuff string in socket buffer, adjust socket data length */
- bcopyff(hellomsg, &s->sdata[s->sdatalen],
- len = strlen(hellomsg));
- s->sdatalen += len;
- /* tell main body the news */
- strcpy(hellomsg, "\r\n Connection starting from [");
- ntoa(&hellomsg[strlen(hellomsg)], s->hisaddr); /* their IP */
- strcat(hellomsg, "].\r\n");
- outs(hellomsg); /* send connection info to main body */
- }
-
-